﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ITAP.Core.Calculations;
using System.Drawing;
using ITAP.ContourBuilder.Components;
using ITAP.ContourBuilder.ContourRender;

using ITAP.ContourBuilder.Manipulation.Tolerance;
using ITAP.ContourBuilder.Manipulation;
using ITAP.Core.Modelling;

namespace ITAP.ContourBuilder
{
    public delegate void SetCursorDelegate(Cursor cursor);

    public delegate Point PointToClientDelegate(Point point);

    public delegate void PointDelegate(Point point);

    public partial class ContourControl : Panel
    {      
        private double defaultScaleForEmptyContainer = 0.3;

        public double DefaultScaleForEmptyContainer
        {
            get { return defaultScaleForEmptyContainer; }
            set { defaultScaleForEmptyContainer = value; }
        }

        private bool disableObtainingPeripheryState = false;

        public bool DisableObtainingPeripheryState
        {
            get { return disableObtainingPeripheryState; }
            set { disableObtainingPeripheryState = value; }
        }

        public RectangleF FullRectangle
        {
            get
            {
                return this.contourContainer.BoundingBox;
            }
        }

        public event PeripheryEvent PeripheryStateChanged;

        private MouseEventArgs currentMouseState = new MouseEventArgs(MouseButtons.None, 0, 0, 0, 0);
        private KeyEventArgs currentKeyState = new KeyEventArgs(Keys.None);

        private MouseMode currentMouseMode;
        private KeyMode currentKeyMode;

        private ToleranceManager toleranceManager;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public ToleranceManager ToleranceManager
        {
            get { return toleranceManager; }
        }

        public bool UseAutoScale
        {
            get { return coordinateConverter.UseAutoScale; }
            set { coordinateConverter.UseAutoScale = value; }
        }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public double ModelScale
        {
            get { return coordinateConverter.Scale; }
            set { coordinateConverter.Scale = value; }
        }

        private ContourManipulator manipulator;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public ContourManipulator Manipulator
        {
            get { return manipulator; }
            set { manipulator = value; }
        }


        private ModelPoint cameraCenter;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public ModelPoint CameraCenter
        {
            get { return cameraCenter; }
            set 
            { 
                cameraCenter = value; 
                this.Invalidate(); 
            }
        }

        private SimpleContourRender render;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public SimpleContourRender Render
        {
            get { return render; }
            set { render = value; }
        }

        private ModelCoordinateConverter coordinateConverter = new ModelCoordinateConverter();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public ModelCoordinateConverter CoordinateConverter
        {
            get { return coordinateConverter; }
            set { coordinateConverter = value; }
        }

        private ContourContainer contourContainer;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public ContourContainer ContourContainer
        {
            get 
            { 
                return contourContainer; 
            }
            set 
            {
                contourContainer = value; 



                if (contourContainer.HasBounds == false && this.coordinateConverter.Scale == 0)
                {
                    this.UseAutoScale = false;
                    this.coordinateConverter.Scale = this.defaultScaleForEmptyContainer;
                }

                ActionSelectMoveChangeDelete defaultAction = new ActionSelectMoveChangeDelete("Перемещение компонент");
                defaultAction.Control = this;
                GlobalData.ExecuteManager.DefaultExecutable = defaultAction;
                GlobalData.ExecuteManager.Execute(defaultAction);

            }
        }

        public ContourControl()
        {
            InitializeComponent();
            this.Init();

        }

        private void Init()
        {
            this.BackColor = Color.White;
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.coordinateConverter = new ModelCoordinateConverter();
            this.render = new SimpleContourRender();
            toleranceManager = new ToleranceManager();
            manipulator = new ContourManipulator(this);
            GlobalData.ExecuteManager = new ExecuteManager(manipulator);
        }

        public ContourControl(IContainer container)
        {
            container.Add(this);

            InitializeComponent();

            this.Init();
        }

        protected override void OnPaint(PaintEventArgs e)
        {        
            base.OnPaint(e);
            if (this.contourContainer == null)
                return;

            RectangleF bbox = this.contourContainer.BoundingBox;

            if (this.cameraCenter == null)
            {
                RectangleF rect = bbox;
                this.cameraCenter = new ModelPoint(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
            }

            e.Graphics.Clear(Color.White);
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            
            render.Initialize(e.Graphics, this.Width, this.Height, bbox, this.coordinateConverter, this.cameraCenter);
            this.contourContainer.SetDisplayParameters(this.Width, this.Height, bbox, this.coordinateConverter, this.cameraCenter);
            this.contourContainer.AcceptVisitor(render);
        }

        protected override void OnResize(EventArgs eventargs)
        {
            base.OnResize(eventargs);
            if (this.contourContainer != null)
            {
                this.contourContainer.SetDisplayParameters(this.Width, this.Height, this.contourContainer.BoundingBox, this.coordinateConverter, this.cameraCenter);
                this.contourContainer.Update();
            }
            
            this.Invalidate();            
        }

        public void ZoomIn(ModelPoint centerPoint, double scaleDelta)
        {
            this.CameraCenter = (ModelPoint)centerPoint.Clone();
            this.ModelScale /= scaleDelta;
            if (this.contourContainer != null)
            {
                this.contourContainer.SetDisplayParameters(this.Width, this.Height, this.contourContainer.BoundingBox, this.coordinateConverter, this.cameraCenter);
                this.contourContainer.Update();
            }
            this.Invalidate();
        }

        public void ZoomOut(ModelPoint centerPoint, double scaleDelta)
        {
            this.CameraCenter = (ModelPoint)centerPoint.Clone();
            this.ModelScale *= scaleDelta;
            if (this.contourContainer != null)
            {
                this.contourContainer.SetDisplayParameters(this.Width, this.Height, this.contourContainer.BoundingBox, this.coordinateConverter, this.cameraCenter);
                this.contourContainer.Update();
            }
            this.Invalidate();
        }

        public void SetCameraToCenter()
        {
            RectangleF rect = this.contourContainer.BoundingBox;
            this.CameraCenter = new ModelPoint(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
        }

        public void UpdateModel()
        {
            this.contourContainer.SetDisplayParameters(this.Width, this.Height, this.contourContainer.BoundingBox, this.coordinateConverter, this.cameraCenter);
            this.ContourContainer.Update();
            this.Invalidate();
        }

        public PointF ModelToScreen(ModelPoint point)
        {
            if (this.cameraCenter == null)
                return new Point(0, 0);
            RectangleF bbox = this.contourContainer.BoundingBox;
            PointF screenPoint = this.coordinateConverter.ModelPointToScreenPoint(point, this.ClientRectangle.Size.Width, this.ClientRectangle.Size.Height, bbox, this.CameraCenter);
            return screenPoint;
        }

        public ModelPoint ScreenToModel(PointF point)
        {
            if (this.cameraCenter == null)
                return new ModelPoint(0, 0);
            RectangleF bbox = this.contourContainer.BoundingBox;
            ModelPoint modelPoint = this.coordinateConverter.ScreenPointToModelPoint(point, this.ClientRectangle.Size.Width, this.ClientRectangle.Size.Height, bbox, this.CameraCenter);
            return modelPoint;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (disableObtainingPeripheryState)
                return;

            this.currentMouseState = e;
            this.currentMouseMode = MouseMode.Down;
            if(this.PeripheryStateChanged != null)
                this.PeripheryStateChanged.Invoke(new GlobalPeripheryState(new MouseState(currentMouseState, currentMouseMode), 
                                                                           new KeyboardState(currentKeyState, currentKeyMode)));
        }

//        private bool manualChangeCursorPosition = false;
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (disableObtainingPeripheryState)
                return;

            this.currentMouseState = e;
            this.currentMouseMode = MouseMode.Move;

            if (this.PeripheryStateChanged != null)
                this.PeripheryStateChanged.Invoke(new GlobalPeripheryState(new MouseState(currentMouseState, currentMouseMode),
                                                                           new KeyboardState(currentKeyState, currentKeyMode)));

            //if (this.cursorMovementDisabled)
            //{
            //    Cursor.Position = disabledCursorPosition;
            //}
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            this.currentMouseState = e;
            this.currentMouseMode = MouseMode.Up;

            if (this.PeripheryStateChanged != null)
                this.PeripheryStateChanged.Invoke(new GlobalPeripheryState(new MouseState(currentMouseState, currentMouseMode),
                                                                           new KeyboardState(currentKeyState, currentKeyMode)));
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (disableObtainingPeripheryState)
                return;

            this.currentMouseState = e;
            this.currentMouseMode = MouseMode.Wheel;

            if (this.PeripheryStateChanged != null)
                this.PeripheryStateChanged.Invoke(new GlobalPeripheryState(new MouseState(currentMouseState, currentMouseMode),
                                                                           new KeyboardState(currentKeyState, currentKeyMode)));
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            this.Focus();
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            this.currentMouseState = e;
            this.currentMouseMode = MouseMode.Click;

            if (this.PeripheryStateChanged != null)
                this.PeripheryStateChanged.Invoke(new GlobalPeripheryState(new MouseState(currentMouseState, currentMouseMode),
                                                                           new KeyboardState(currentKeyState, currentKeyMode)));
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {           
            base.OnKeyDown(e);
            if (disableObtainingPeripheryState)
                return;

            this.currentKeyState = e;
            this.currentKeyMode = KeyMode.Down;

            if (this.PeripheryStateChanged != null)
                this.PeripheryStateChanged.Invoke(new GlobalPeripheryState(new MouseState(currentMouseState, currentMouseMode),
                                                                           new KeyboardState(currentKeyState, currentKeyMode)));
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            if (disableObtainingPeripheryState)
                return;

            this.currentKeyState = new KeyEventArgs(Keys.None);
            this.currentKeyMode = KeyMode.Up;
        }

        public void SetCursor(Cursor cursor)
        {
            this.Cursor = cursor;
        }

        public void SaveToDxf(string fileName)
        {
            this.contourContainer.SaveToDxf(fileName);
        }

        public void LoadFromDxf(string fileName)
        {
            this.contourContainer.LoadFromDxf(fileName);
            this.SetCameraToCenter();
            this.coordinateConverter.UseAutoScale = true;
            
            
            this.UpdateModel();

        }

        public void UpdateTolerance()
        {
            this.ToleranceManager.Visit(this.contourContainer);
        }
    }
}
